package com.hongye.auth.controller;

import cn.hutool.http.HttpUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hongye.auth.entity.RoleUserEntity;
import com.hongye.auth.entity.UserEntity;
import com.hongye.auth.feign.ThirdServer;
import com.hongye.auth.service.LoginServer;
import com.hongye.auth.util.JwtUtil;
import com.hongye.auth.vo.Consatnt;
import com.hongye.auth.vo.GiteeAccessToken;
import com.hongye.auth.vo.GiteeUser;
import com.hongye.common.constants.Constant;
import com.hongye.common.utils.ResponseResult;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import javax.xml.transform.Result;
import java.io.IOException;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

/**
 * @author hxy
 * @email 3199037134@qq.com
 * @date 2022/4/19 14:10
 */
@Controller
@Log4j2
@RequestMapping("auth/login")
public class LoginController {


     @Autowired
    LoginServer loginServer;

     @Autowired
    ThirdServer thirdServer;


     //redis
     @Autowired
    StringRedisTemplate redisTemplate;

     //jwt
     @Autowired
    JwtUtil jwtUtil;


    /**
     * phoneRegularExpression  :手机号正则，错误提示“手机号不正确”
     * @param phone
     * @return
     */
    @RequestMapping("phoneRegularExpression")
    @ResponseBody
    public ResponseResult phoneRegularExpression(@RequestParam("phone") String phone){
        String regex="^((13[0-9])|(14[5-8])|(15([0-3]|[5-9]))|(16[6])|(17[0|4|6|7|8])|(18[0-9])|(19[8-9]))\\d{8}$";
       //判断手机号不为空
        if((phone!=null) && (!phone.isEmpty())){
            //正则
            boolean matches = phone.matches(regex);
            if(matches){
                log.info("手机号判断成功,正则成功");
                return  new ResponseResult(true,200,Constant.Login.PHONE_REGULAR_SUCCESS,phone);
            }else {
                log.info("手机号不为空！！但是手机正则不通过！！");
                return  new ResponseResult(false,500,Constant.Login.PHONE_REGULAR_FALSE,phone);
            }
        }
        log.info("手机号不为空");
        return  new ResponseResult(false,500,Constant.Login.PHONE_REGULAR_NULL,phone);
    }


    /**
     * 通过手机号来查询用户
     * @param phone
     * @return
     */
    @RequestMapping("findUserMsg")
    @ResponseBody
    public  ResponseResult findUserMsg(@RequestParam("phone") String phone){
        UserEntity user=loginServer.findUserMsg(phone);
        if(user!=null){
            //查询成功
            return  new ResponseResult(true,200,Constant.Login.FIND_USER_MSG_SUCCESS,user);
        }
        //查询失败
        return  new ResponseResult(false,500,Constant.Login.FIND_USER_MSG_FALSE);
    }


    /**
     * judgeUserIsBroker 判断手机号是否是经纪人
     * 查询
     * @param phone
     * @return
     */
    @RequestMapping("IfUserIsBroker")
    @ResponseBody
    public  ResponseResult IfUserIsBroker(@RequestParam("phone") String phone){
        //查询手机号 进行查询
        ResponseResult responseResult = findUserMsg(phone);
        UserEntity user = (UserEntity) responseResult.getData();
        System.out.println("获取用户的信息"+user);
        if(user!=null){
            //进行查询中间表
            RoleUserEntity userEntity =loginServer.IfUserIsBroker(user.getId());
            if(userEntity!=null){
                return new ResponseResult(true,200,Constant.Login.IF_USER_IS_BROKER_SUCCESS);
            }
            return new ResponseResult(false,500,Constant.Login.IF_USER_IS_BROKER_FALSE);
        }
        //返回 通过手机号来查询用户
        return  responseResult;
    }


    /**
     * 手机号验证成功 进行 点击发送验证码 SendVerificationCode
     * 调用第三方微服务
     * @param phone
     * @return
     */
    @RequestMapping("SendPhoneCode")
    @ResponseBody
    public  ResponseResult SendVerificationCode(@RequestParam("phone") String phone) throws InterruptedException {
        //验证码
        String code = null;
        //获取Redis中 手机号
        String redisUserPhone = redisTemplate.opsForValue().get(Constant.UserPhoneRedis.REDIS_PHONE);
         log.info("redis中手机号"+redisUserPhone);
        if(redisUserPhone==null){
            //进行获取code
            code = thirdServer.sendPhoneMsg(phone);
            //先将手机号进行存入redis中  1分钟
            redisTemplate.opsForValue().set(Constant.UserPhoneRedis.REDIS_PHONE,phone,1,TimeUnit.MINUTES);
            //进行将手机号作为键值 存入 code  1分钟
            redisTemplate.opsForValue().set(Constant.UserPhoneRedis.REDIS_Code+"-"+phone,code,1,TimeUnit.MINUTES);
            //返回值
            if(StringUtils.isNotEmpty(code)){
                return new ResponseResult(true,200,Constant.Login.SEND_PHONE_CODE_SUCCESS,code);
            }
                return new ResponseResult(true,500,Constant.Login.SEND_PHONE_CODE_FALSE);
            }
            //有的话获取code
            String redisCode= redisTemplate.opsForValue().get(Constant.UserPhoneRedis.REDIS_Code +"-"+ phone);
            log.info("redis中code：",redisCode);
            //进行判断  发送过code 的
            //因为 60秒 redis 中所以 能取出来 就说明 没有超过
            if(StringUtils.isNotEmpty(redisCode)){
                return new ResponseResult(true,200,Constant.Login.SEND_PHONE_CODE_TIME_NOT);
            }
        return new ResponseResult(false,500,Constant.Login.SEND_PHONE_CODE_TIME_OK);
    }



    /** 手机号  验证码 进行登录
     * 进行登录 经济人
     * 因为手机号设置了唯一索引  一个人对应一个手机号 不太可能出现重复的
     * @param phone
     * @param code
     * @return
     */
    @RequestMapping("brokerLogin")
    @ResponseBody
    public ResponseResult brokerLogin(@RequestParam("phone") String phone,
                                      @RequestParam("code") String code){
        System.out.println("接收的登陆数据手机号："+phone+"  验证码 ："+code);

        //进行 验证登录 判断 redis中数据是否还有 1分钟过期
        //获取Redis中 手机号
        String redisUserPhone = redisTemplate.opsForValue().get(Constant.UserPhoneRedis.REDIS_PHONE);
        System.out.println("redis手机号"+redisUserPhone);
        //通过手机号进行去查询code
       if(StringUtils.isNotEmpty(redisUserPhone)){
           //有的话获取code
           String redisCode= redisTemplate.opsForValue().get(Constant.UserPhoneRedis.REDIS_Code +"-"+ phone);
           System.out.println("redisCode的code："+redisCode);
           //进行校验证这两个验证码是否正确
           if(redisCode.equals(code)){
               System.out.println("验证码验证成功");
               //进行查询个人数据 返回的时候数据
               UserEntity userMsg = loginServer.findUserMsg(phone);
               //返回用户登录成功的欢迎语
               return new ResponseResult(true,200,Constant.Login.BROKER_LOGIN_WELCOME+userMsg.getUserName()+Constant.Login.BROKER_LOGIN_SUCCESS);
           }
           //验证码不对进行返回
           return new ResponseResult(false,500,Constant.Login.BROKER_LOGIN_FALSE);
       }
       //redis存入的手机号为空
        return new ResponseResult(false,500,Constant.Login.BROKER_LOGIN_NOT_PHONE);
    }


    /**
     * 第二种登陆方式
     * 场景描述  用户手机号  密码  进行登录
     * 普通的登录
     */
    @RequestMapping("brokerPhoneAndPasswordLogin")
    @ResponseBody
    public ResponseResult brokerPhoneAndPasswordLogin(@RequestParam("userPhone") String userPhone,
                                                      @RequestParam("userPwd") String userPwd) throws Exception {
        //查询手机号
        ResponseResult userMsg = findUserMsg(userPhone);
        //查询用户
        UserEntity user = (UserEntity) userMsg.getData();
        //密码登录    （进行登录 密码加密用到md5工具类）
        String oldPwd = user.getUserPwd();
         //非对称加密
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        boolean matches = bCryptPasswordEncoder.matches(userPwd, oldPwd);
        //成功
        if(matches){
            //成功的话进行 生成token 鉴权
            String token = getToken(user.getName(), user.getUserPwd());
            return new ResponseResult(true,200,Constant.Login.LOGIN_WELCOME,token);
        }
        //失败
        return new ResponseResult(false,500,Constant.Login.LOGIN_SUCCESS);
    }



    /** 不是请求只是调用
     * 鉴权使用jwt生成token
     */
     public String getToken(String userName,String userPwd){
          //进行生成
         String token = jwtUtil.createJWT(userName, userPwd, "user");
         return  token;
     }


    /**
     *  非对称加密
     * @param pwd
     * @return
     * @throws Exception
     */
//    @RequestMapping("loginPwd")
//    @ResponseBody
    public String loginPwd(String pwd) throws Exception {
           //非对称加密
         BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
         String encode = bCryptPasswordEncoder.encode(pwd);
        return encode;
    }

    /**
     * 第三方登录
     *  跳入界面  获取路径  获取完路径
     */
    @RequestMapping("/getUrl")
    @ResponseBody
    public ResponseResult getUrl(){
        //StringBuffer 类的 append() 方法用于向原有 StringBuffer 对象中追加字符串
        //线程安全
        //我们如果对字符串进行拼接操作，每次拼接都会构成一个新的String对象，既耗时，又浪费空间
        //前者长度和内容可变，后者不可变
        //可以把任意类型数据添加到字符串缓冲区里面，并返回字符串缓冲区本身
        StringBuffer stringBuffer = new StringBuffer();
        //进行追加
        stringBuffer.append(Consatnt.AUTHURL)
                .append("client_id").append("=").append(Consatnt.CLIENTID).append("&")
                .append("redirect_uri").append("=").append(Consatnt.REDIRECTURI).append("&")
                .append("response_type").append("=").append("code");
        //变成路径
        String url = stringBuffer.toString();
        System.out.println(url);
        log.info("第三方获取地址进行",url);
        //不为空
        if(StringUtils.isNotEmpty(url)){
            //返回成功
            return new ResponseResult(false,200,Constant.Login.THIRD_GETURL_SUCCESS,url);
        }
        //返回失败
        return new ResponseResult(true,500,Constant.Login.THIRD_GETURL_FALSE);
    }

    /**
     * 进行第三方登录进行回调
     * @param code
     * @param model
     * @return
     * @throws JsonProcessingException
     */
    @RequestMapping("/success")
    private String getUserInfo(String code, Model model) throws IOException {
        //回掉进行获取code
        System.out.println("code: "+code);
        //判断code是否为空
        if(code!=null){

            //将参数放入HashMap
            HashMap<String, Object> paramMap = new HashMap<>();
            paramMap.put("grant_type","authorization_code");
            paramMap.put("redirect_uri", Consatnt.REDIRECTURI);
            paramMap.put("code", code);
            paramMap.put("client_id",Consatnt.CLIENTID);
            paramMap.put("client_secret",Consatnt.CLIENTSECRET);

            /**
             * 发起一个post请求
             */
            String respBody = HttpUtil.post(Consatnt.TOKENURL, paramMap);
//        System.out.println("respBody=" +respBody);

            /**
             * 解析返回的数据,取出 access_token
             * {"access_token":"5568eb932eca72f697e893f4a2851c8d","token_type":"bearer","expires_in":86400,"refresh_token":"720b47425c061029ec7659efefd75bb8165389ec637a51932774af4b91dec52c","scope":"user_info","created_at":1631779380}
             * access_token = 5568eb932eca72f697e893f4a2851c8d
             * ObjectMapper也可以从JSON数组字符串中读取一组Java对象
             */
            ObjectMapper om = new ObjectMapper();
            GiteeAccessToken accessToken = om.readValue(respBody, GiteeAccessToken.class);
            System.out.println("accessToken :"+accessToken);
            String access_token = accessToken.getAccess_token();
            System.out.println("access_token :"+access_token);


            /**
             * 获取到的 令牌token 就可以请求 Gitee 的 api了
             * 比如 获取授权用户的资料, https://gitee.com/api/v5/user?access_token=5568eb932eca72f697e893f4a2851c8d
             */
            String getUserInfoUrl = "https://gitee.com/api/v5/user" + "?" + "access_token" + "=" + access_token;
            String userInfo = HttpUtil.get(getUserInfoUrl);
//        System.out.println("userInfo = " + userInfo);

            GiteeUser giteeUser = om.readValue(userInfo, GiteeUser.class);
            System.out.println("giteeUser: "+giteeUser);

            //返回到前端进行页面
            model.addAttribute("name",giteeUser.getName());

            model.addAttribute("avatarUrl",giteeUser.getAvatar_url());

            log.info("登录失败");

            return "success";
        }
        log.info("登录失败");

        return "fail";
    }
}
